<?php
/**
 * Copyright (c) 2006-2008, Julien PORTALIER
 * http://featherphp.googlecode.com/
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 */

/**
 * Functions to manipulate some XML.
 */
class xml
{
	/**
	 * Translates an array to an XML string.
	 */
	static function transform($arr, $tag)
	{
		ob_start();
		if (is_array($arr) and isset($arr[0]))
		{
			# list of items
			$_tag = Inflector::tableize($tag);
			foreach($arr as $v)
			{
				if (is_array($v) or ($v instanceof Iterator or $v instanceof  IteratorAggregate))
				{
					echo "\n<{$_tag}>";
					echo self::transform($v, $tag);
					echo "\n</{$_tag}>";
				}
				else {
					echo "\n\t<{$_tag}>".htmlspecialchars($v)."</{$_tag}>";
				}
			}
		}
		elseif ((is_array($arr) and !empty($arr))
			or (($arr instanceof Iterator or $arr instanceof  IteratorAggregate) and iterator_count($arr) > 0))
		{
			# item
			$tag = Inflector::singularize(Inflector::underscore($tag));
			echo "\n<$tag>";
			
			foreach($arr as $k => $v)
			{
				$k = Inflector::underscore($k);
				if (is_array($v) or ($v instanceof Iterator or $v instanceof IteratorAggregate))
				{
					#echo (!empty($v)) ? self::transform($v, $k) : "\n\t<$k/>";
					if (!empty($v) or iterator_count($v) > 0)
					{
						$k = Inflector::singularize(Inflector::underscore($k));
						foreach($v as $vv) {
							echo "\n\t<$k>$vv</$k>";
						}
					}
#					else {
#						echo "\n\t<$k/>";
#					}
				}
				else
				{
					echo (empty($v) and strlen($v) === 0) ? "\n\t<$k/>" :
						"\n\t<$k>".htmlspecialchars($v)."</$k>";
				}
			}
			echo "\n</$tag>";
		}
		else {
			echo "<$tag/>\n";
		}
		return ob_get_clean();
	}
	
	/**
	 * Transforms an XML string to array.
	 * @return Array
	 * @param $xmlstr String
	 */
	static function & from_string($xmlstr)
	{
		$parser = new Feather_XMLParser();
		$xml    = $parser->parse($xmlstr);
		unset($parser);
		return $xml;
	}
	
	/**
	 * Transforms an XML file to array.
	 * 
	 * @return Array
	 * @param $filename String
	 */
	static function & from_file($filename)
	{
		$xmlstr = file_get_contents($filename);
		return self::from_string($xmlstr);
	}
}

/**
 * ...
 */
class Feather_XMLParser
{
	protected $data  = array();
	protected $value = '';
	protected $depth = -1;
	protected $parser;
	
	function __construct()
	{
		$this->parser = xml_parser_create();
		xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
		xml_set_element_handler($this->parser, array($this, '_start_element'), array($this, '_end_element'));
		xml_set_character_data_handler($this->parser, array($this, '_character_data'));
	}
	
	function __destruct()
	{
		if ($this->parser)
		{
			xml_parser_free($this->parser);
		}
	}
	
	function & parse($xmlstr)
	{
	    if (!xml_parse($this->parser, $xmlstr, true))
		{
			throw new Exception(sprintf("Erreur XML: %s at line %d",
                xml_error_string(xml_get_error_code($this->parser)),
                xml_get_current_line_number($this->parser)), 500);
	    }
		return $this->data;
	}
	
	function _start_element($parser, $name, $attrs)
	{
		$this->value = '';
		$this->depth++;
	}
	
	function _end_element($parser, $name)
	{
		if ($this->depth > 0)
		{
			$this->data[$name] = $this->value;
		}
		$this->depth--;
	}
	
	function _character_data($parser, $chr)
	{
		$this->value .= $chr;
	}
}
?>